<!-- pwa 更新组件 -->
<!-- pwa 更新组件 -->
<template>
  <div v-if="showOffline || needRefresh" style="position: fixed; right: 20px; bottom: 20px; z-index: 33333">
    <transition name="el-fade-in-linear">
      <div v-if="showOffline && !needRefresh" :style="{ '--base-fontsize': newSize }" class="pwa-reload-rrompt">
        <div class="icon-box">
          <icon-solar-rocket-outline></icon-solar-rocket-outline>
        </div>
        <div class="pwa-reload-content">
          <div class="box">
            <span>本网站以支持PWA应用,是否需要添加？</span>
          </div>
          <div class="box butt-box">
            <el-button type="warning" @click="installApp">添加应用</el-button>
            <el-button type="warning" plain @click="closeInstall">取消</el-button>
          </div>
        </div>
      </div>
    </transition>
    <transition name="el-fade-in-linear">
      <div v-if="needRefresh" :style="{ '--base-fontsize': newSize }" class="pwa-reload-rrompt">
        <div class="icon-box">
          <icon-solar-widget-add-linear></icon-solar-widget-add-linear>
        </div>
        <div class="pwa-reload-content">
          <div class="box">
            <span>有新的版本,是否立即更新？</span>
          </div>
          <div class="box butt-box">
            <el-button type="warning" @click="updateAPP">立即更新</el-button>
            <el-button type="warning" plain @click="notUpdate">稍后更新</el-button>
          </div>
        </div>
      </div>
    </transition>
  </div>
</template>

<script setup lang="ts">
  import IconSolarWidgetAddLinear from '~icons/solar/widget-add-linear';
  import IconSolarRocketOutline from '~icons/solar/rocket-outline';
  import { useRegisterSW } from 'virtual:pwa-register/vue';
  import { IsInstallPwaStorage } from '@/utils/storage/localStorage';
  const newSize = computed(() => {
    const globalSize = useGlobalStore().globalSize;
    return globalSize === 'small' ? 'var(--el-font-size-base)' : globalSize === 'large' ? 'var(--el-font-size-large)' : 'var(--el-font-size-medium)';
  });
  const isInstallStorage = new IsInstallPwaStorage();

  /** 检测更新的定时器 */
  let updateTimeout = undefined as NodeJS.Timeout | undefined;

  /** 保存安装应用事件(用于安装) */
  let _BEFORE_INSTALL_EVENT = null as $BeforeinstallpromptEvent | null;

  /** 监听应用可安装前 */
  window.addEventListener('beforeinstallprompt', beforeinstallpromptEvent);

  /** 监听安装完成 */
  window.addEventListener('appinstalled', () => {
    isInstallStorage.set(true);
  });

  /** 注册PWA更新 */
  const { offlineReady, needRefresh, updateServiceWorker } = useRegisterSW({
    onRegisteredSW(swUrl, r) {
      r && checkUpdate(swUrl, r);
    },
    onRegisterError(err) {
      console.log('PWA注册失败 :>> ', err);
    },
  });

  /** 是否显示安装APWA应用提示 */
  const showOffline = computed(() => isInstallStorage.get() === null && offlineReady.value);

  onUnmounted(() => {
    updateTimeout && clearTimeout(updateTimeout);
  });

  /**
   * 检测更新
   */
  async function checkUpdate(swUrl: string, r: ServiceWorkerRegistration) {
    try {
      if (!(!r.installing && navigator)) {
        delayCheckUpdate(swUrl, r);
        return;
      }

      if ('connection' in navigator && !navigator.onLine) {
        delayCheckUpdate(swUrl, r);
        return;
      }
      const resp = await fetch(swUrl, { cache: 'no-store', headers: { 'cache': 'no-store', 'cache-control': 'no-cache' } });
      if (resp?.status === 200) {
        await r.update();
      }

      delayCheckUpdate(swUrl, r);
    } catch (err) {
      delayCheckUpdate(swUrl, r);
      console.error('PWA检测更新出错===========', err);
    }
  }

  /**
   * 延迟检测更新
   */
  function delayCheckUpdate(swUrl: string, r: ServiceWorkerRegistration) {
    updateTimeout = setTimeout(
      () => {
        checkUpdate(swUrl, r);
      },
      1000 * 60 * 10
    );
  }

  /**
   * 安装为WPA应用
   */
  function installApp() {
    window.removeEventListener('beforeinstallprompt', beforeinstallpromptEvent);
    offlineReady.value = false;
    _BEFORE_INSTALL_EVENT?.prompt?.(); // 显示系统的安装弹出框
    _BEFORE_INSTALL_EVENT = null;
  }

  /**
   * 稍后安装为WPA应用
   */
  function closeInstall() {
    isInstallStorage.set(false);
    offlineReady.value = false;
    window.removeEventListener('beforeinstallprompt', beforeinstallpromptEvent);
  }

  /**
   * 更新网站
   */
  function updateAPP() {
    updateTimeout && clearTimeout(updateTimeout);
    needRefresh.value = false;
    updateServiceWorker?.();
  }

  /**
   * 稍后更新网站
   */
  function notUpdate() {
    _BEFORE_INSTALL_EVENT && clearTimeout(updateTimeout);
    needRefresh.value = false;
  }

  /**
   * 监听可安装前的函数
   */
  function beforeinstallpromptEvent(e: $BeforeinstallpromptEvent) {
    e.preventDefault(); // 阻止默认安装条出现在手机上
    offlineReady.value = true;
    _BEFORE_INSTALL_EVENT = e;
  }
</script>

<style scoped lang="scss">
  .pwa-reload-rrompt {
    @include flex(row, nowrap, flex-start, center, center);
    padding: 8px 15px;
    margin-bottom: 20px;
    color: var(--el-color-warning);
    background: var(--el-color-warning-light-9);
    border: 1px solid var(--el-color-warning-light-7);
    border-radius: var(--el-border-radius-base);
    box-shadow: var(--el-box-shadow-light);

    &:last-child {
      margin-bottom: 0;
    }

    .icon-box {
      margin-right: 15px;
      font-size: 20px;
    }

    .pwa-reload-content {
      @include flex(column, nowrap, flex-start, flex-start, center);

      .box {
        padding: 6px 0;
        font-size: var(--base-fontsize);
      }
    }
  }
</style>
